﻿Imports System.Collections.Generic
Imports System.Threading
Imports Microsoft.Office.Interop
'Imports RTMOSync.Office

Friend Class SyncManager

#Region "Fields"
    Private Shared IsSyncing As Boolean = False
    Private Shared OldDate As Date = New Date(1901, 1, 1)

    Dim IsFullSync As Boolean = False
    Dim NoneDate As Date = New Date(4501, 1, 1)
    Dim PatternEndDate As Date = Date.Parse("12/31/4500 11:59:00 PM")
    Dim count As Integer = 0
    Dim TotalTasksCounts As Integer
    Private outlookTasksAll As Outlook.Items
    Dim outlookTasks As List(Of Outlook.TaskItem)
    Dim rtmTaskLists As List(Of RTMnet.TaskList)

    Dim rtmTaskSeries As List(Of RTMnet.TaskSeries)
    'Dim TasksInRTM As List(Of RTMnet.TaskSeries)
    Dim myrtm As RTMnet.RTM


    'Dim rtmTasksNames As List(Of String)
    'Dim outlookTaskNames As List(Of String)
    'Dim TasksInRTM As List(Of RTMnet.TaskList)
    'Dim TasksInOutlook As List(Of Outlook.TaskItem)

    Dim WithEvents BackgroundWorker1 As System.ComponentModel.BackgroundWorker
    Dim WithEvents delayTimer As System.Windows.Forms.Timer

    Dim alreadyRan As Boolean = False
#End Region

#Region "Event Definitions"
    Public Event BeforeSyncRTMOnlyTasks()
    Public Event BeforeSyncOutlookOnlyTasks()
    Public Event BeforeSyncDuplicateTasks()
    Public Event BeforeSync()
    Public Event BeforeDataLoad()

    Public Event AfterSyncRTMOnlyTasks()
    Public Event AfterSyncOutlookOnlyTasks()
    Public Event AfterSyncDuplicateTasks()
    Public Event AfterSync()
    Public Event AfterDataLoad(ByVal TaskCount As Integer)

    Public Event ReportProgress(ByVal Count As Integer)
#End Region

#Region "Constructors & startup logic"

    Private Sub New()
        myrtm = New RTMnet.RTM(My.Settings.ApiKey, sharedsecret, My.Settings.Token)
        Me.BackgroundWorker1 = New System.ComponentModel.BackgroundWorker()
        Me.BackgroundWorker1.WorkerReportsProgress = True
        Me.BackgroundWorker1.WorkerSupportsCancellation = True
        Me.delayTimer = New System.Windows.Forms.Timer
        With delayTimer
            .Interval = 200
            .Enabled = True
        End With

    End Sub

    Friend Shared Function StartSync() As SyncManager
        If SyncedItems.Count = 0 Then
            'can't find any synced itmes records
            'todo might be a bug... try to start initial sync from the initial sync form only
            Return StartFullSync()
        Else
            Return StartIncrementalSync()
        End If
    End Function

    Private Shared Function StartIncrementalSync() As SyncManager
        Dim _instance As SyncManager = New SyncManager
        With _instance
            .IsFullSync = False
            .delayTimer.Start()
        End With
        Return _instance
    End Function
    Friend Shared Function StartFullSync() As SyncManager
        DeleteSyncedItems()
        My.Settings.LastSync = OldDate

        Dim _instance As SyncManager = New SyncManager
        With _instance
            .IsFullSync = True
            .delayTimer.Start()
        End With
        Return _instance
    End Function

    Sub delayTimer_Tick() Handles delayTimer.Tick
        delayTimer.Enabled = False
        delayTimer.Stop()
        'added safety
        If alreadyRan = False Then
            alreadyRan = True
            Me.BackgroundWorker1.RunWorkerAsync()
        End If
    End Sub

    Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        LoadData()
        Begin()
        'If IsInitialSync Then
        '    LoadData()
        '    Begin()
        'Else
        '    'perform incremental sync
        '    LoadData(My.Settings.LastSync)
        '    Begin()
        'End If
    End Sub

    Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted

    End Sub
#End Region

    Private Sub Begin()
        Try
            If Not IsSyncing Then
                IsSyncing = True
                RaiseEvent BeforeSync()
                If IsFullSync Then
                    SyncCategories()
                    SyncMatchedItems()
                    SyncOutlookOnlyTasks()
                    SyncRTMOnlyTasks()
                Else
                    'incremental sync
                    Dim filteredRTMTasks As List(Of RTMnet.TaskSeries)
                    Dim filteredOutlookTasks As List(Of Outlook.TaskItem)
                    Dim UnSyncedItems As New SyncedItemsList
                    'loop through synced items
                    filteredRTMTasks = (From series In rtmTaskSeries _
                                        Join syncitem In SyncedItems _
                                        On syncitem.TaskSeriesId Equals series.TaskSeriesID _
                                        Select series).ToList
                    filteredOutlookTasks = (From task As Outlook.TaskItem In outlookTasksAll _
                                            Join syncitem In SyncedItems _
                                            On syncitem.OutlookId Equals task.EntryID _
                                            Select task).ToList
                    Dim outlookTasksToDelete As New List(Of Outlook.TaskItem)
                    For i As Integer = SyncedItems.Count - 1 To 0 Step -1
                        Dim item As RTMOSync.SyncItem = SyncedItems(i)
                        Dim taskseries As RTMnet.TaskSeries = filteredRTMTasks.GetTaskSeries(item.TaskSeriesId)
                        Dim outlooktask As Outlook.TaskItem = filteredOutlookTasks.GetTask(item.OutlookId)

                        If IsNothing(taskseries) Then
                            If Not IsNothing(outlooktask) Then
                                'item has been deleted from rtm, so delete from outlook
                                outlookTasksToDelete.Add(outlooktask)
                            Else
                                'item was already deleted from outlook, so remove from synced items
                                UnSyncedItems.Add(item)
                                SyncedItems.RemoveAt(i)
                            End If
                        Else
                            'item still in rtm, is it still in outlook?
                            If IsNothing(outlooktask) Then
                                'item has been delete from outlook, so delete from rtm
                                myrtm.TasksDelete(item.Listid, item.TaskSeriesId, item.TaskId)
                                UnSyncedItems.Add(item)
                                SyncedItems.RemoveAt(i)
                            Else
                                'task is still in outlook and rtm, do we need to sync it?
                                Dim needsSync As Boolean = False
                                needsSync = ShouldISync(taskseries, outlooktask)
                                If needsSync Then
                                    'something changed since last sync, so we need to sync
                                    Dim mytask = New Task(taskseries, item.Listid)
                                    Dim ctask = New CompoundTask(mytask, outlooktask)
                                    SyncMatchedItem(ctask, SyncedItems(i))
                                    mytask = Nothing
                                    ctask = Nothing
                                Else
                                    'update sync time
                                    item.SyncedOn = Now
                                End If
                            End If
                        End If
                        taskseries = Nothing
                        outlooktask = Nothing
                    Next

                    'lets find new items i.e. items not in SyncedItems
                    'get non-completed tasks that are not in syncedItems
                    filteredRTMTasks = (From series In rtmTaskSeries _
                                        Where String.IsNullOrEmpty(series.TaskInfo.First.Completed) _
                                         AndAlso Not (SyncedItems.Contains(series.TaskSeriesID, series.TaskInfo.First.ID)) _
                                         AndAlso Not (UnSyncedItems.Contains(series.TaskSeriesID, series.TaskInfo.First.ID)) _
                                         Select series).ToList
                    filteredOutlookTasks = (From task As Outlook.TaskItem In outlookTasksAll _
                                           Where task.Complete = False _
                                           AndAlso Not (SyncedItems.Contains(task)) _
                                           AndAlso Not (UnSyncedItems.Contains(task)) _
                                            Select task).ToList
                    Dim matchedNewTasks = (From series In filteredRTMTasks _
                                           Join otask In filteredOutlookTasks _
                                           On series.Name.Trim Equals otask.Subject.Trim _
                                           Select New CompoundTask(New Task(series, GetListId(series)), otask)).ToList
                    'these are new tasks added with the same name in outlook and rtm
                    For Each item In matchedNewTasks
                        SyncMatchedItem(item)
                    Next

                    'refilter again to make sure there are no
                    filteredRTMTasks = (From series In rtmTaskSeries _
                                        Where String.IsNullOrEmpty(series.TaskInfo.First.Completed) _
                                         AndAlso Not (SyncedItems.Contains(series.TaskSeriesID, series.TaskInfo.First.ID)) _
                                         AndAlso Not (UnSyncedItems.Contains(series.TaskSeriesID, series.TaskInfo.First.ID)) _
                                         Select series).ToList
                    filteredOutlookTasks = (From task As Outlook.TaskItem In outlookTasksAll _
                                           Where task.Complete = False _
                                           AndAlso Not (SyncedItems.Contains(task)) _
                                           AndAlso Not (UnSyncedItems.Contains(task)) _
                                            Select task).ToList
                    For Each series In filteredRTMTasks
                        If Not SyncedItems.Contains(series.TaskSeriesID, series.TaskInfo.First.ID) Then
                            CreateAndSyncOutlook(series)
                        End If
                    Next
                    For Each outlookTask In filteredOutlookTasks
                        If Not SyncedItems.Contains(outlookTask) Then
                            'CreateRTMFromOutlook(task)
                            CreateAndSyncRTM(outlookTask)
                        End If
                    Next
                    'delete outlook tasks
                    For j As Integer = outlookTasksToDelete.Count - 1 To 0 Step -1
                        SyncedItems.Delete(outlookTasksToDelete(j))
                        outlookTasksToDelete(j).Delete()
                    Next
                    'clean up
                    filteredOutlookTasks = Nothing
                    filteredRTMTasks = Nothing
                    matchedNewTasks = Nothing
                    outlookTasksToDelete = Nothing
                    UnSyncedItems = Nothing
                End If
                'save sync info to file
                SaveSyncedItems()
                My.Settings.LastSync = Now 'add a couple of seconds (just in case)
            End If
        Finally
            RaiseEvent AfterSync()
            IsSyncing = False
            My.Settings.Save()

            'cleanup
            outlookTasks = Nothing
            outlookTasksAll = Nothing
            rtmTaskSeries = Nothing
            rtmTaskLists = Nothing
            GC.Collect()
        End Try
    End Sub

    Private Function ShouldISync(ByVal taskseries As RTMnet.TaskSeries, ByVal outlookTask As Outlook.TaskItem) As Boolean
        'check name
        If taskseries.Name.Trim <> outlookTask.Subject.Trim Then
            Return True
        End If

        'check due date
        If taskseries.TaskInfo.First.Due_raw = String.Empty AndAlso _
            outlookTask.DueDate = NoneDate Then
            'dates are the same do nothing
        Else
            If taskseries.TaskInfo.First.Due <> outlookTask.DueDate Then
                Return True
            End If
        End If

        'check categories and task
        If Not IsNothing(taskseries.Tags.TagCollection) OrElse Not IsNothing(outlookTask.Categories) Then
            Dim cats As String = ""
            For Each rtmTag In taskseries.Tags.TagCollection
                If cats.Length = 0 Then
                    cats = rtmTag.Text
                Else
                    cats += "," + rtmTag.Text
                End If
            Next
            If cats <> outlookTask.Categories.Replace(" ", "") Then
                Return True
            End If
        End If

        'check priority
        Select Case taskseries.TaskInfo.First.Priority
            Case "1"
                If outlookTask.Importance <> Outlook.OlImportance.olImportanceHigh Then
                    Return True
                End If
            Case "2"
                If outlookTask.Importance <> Outlook.OlImportance.olImportanceNormal Then
                    Return True
                End If
            Case "3"
                If outlookTask.Importance <> Outlook.OlImportance.olImportanceLow Then
                    Return True
                End If
            Case Else
                If outlookTask.Importance <> Outlook.OlImportance.olImportanceNormal Then
                    Return True
                End If
        End Select
        'check complete
        If outlookTask.Complete AndAlso _
            taskseries.TaskInfo.First.Completed.Length = 0 Then
            Return True
        ElseIf outlookTask.Complete = False AndAlso _
            taskseries.TaskInfo.First.Completed.Length > 0 Then
            Return True
        End If

        'check recurrence
        If IsNothing(taskseries.Recurrence) AndAlso outlookTask.IsRepeating = False Then
            'no recurrence
        Else
            If IsNothing(taskseries.Recurrence) OrElse outlookTask.IsRepeating = False Then
                'one of them had the recurrence removed
                Return True
            Else
                'there is a recurrence
                Dim rec As Outlook.RecurrencePattern = outlookTask.GetRecurrencePattern
                If rec.DayOfWeekMask <> taskseries.Recurrence.DayOfWeekMask Then
                    Return True
                End If
                If rec.Interval <> taskseries.Recurrence.Interval Then
                    Return True
                End If
                Select Case taskseries.Recurrence.Freq
                    Case RTMnet.Recurrence.Frequencies.DAILY
                        If rec.RecurrenceType <> Outlook.OlRecurrenceType.olRecursDaily Then
                            Return True
                        End If
                    Case RTMnet.Recurrence.Frequencies.WEEKLY
                        If rec.RecurrenceType <> Outlook.OlRecurrenceType.olRecursWeekly Then
                            Return True
                        End If
                    Case RTMnet.Recurrence.Frequencies.MONTHLY
                        If rec.RecurrenceType <> Outlook.OlRecurrenceType.olRecursMonthly Then
                            Return True
                        End If
                    Case Else
                        Debug.WriteLine(taskseries.Recurrence.Repeat & taskseries.Recurrence.Freq)
                End Select

                If rec.NoEndDate = False OrElse taskseries.Recurrence.Until <> Date.MinValue Then
                    'there is an end date
                    If rec.PatternEndDate <> taskseries.Recurrence.Until Then
                        Return True
                    End If
                    'If rec.Occurrences <> taskseries.Recurrence.Count Then
                    '    Return True
                    'End If
                End If
            End If
        End If
        'todo check notes

        'todo check reminder/time

        'no need for sync
        Return False
    End Function

    Private Sub LoadData()
        Try
            MyLog.Log("Begin Load Data")
            RaiseEvent BeforeDataLoad()

            MyLog.Log("Get RTM Tasks")
            'get rtm tasks
            Dim rtmTasks As RTMnet.Tasks = myrtm.TasksGetList()
            If Not IsNothing(rtmTasks) Then
                rtmTaskLists = (From list In rtmTasks.TaskListCollection _
                                Where Not IsNothing(list.TaskCollection) _
                                ).ToList
                rtmTaskSeries = (From list In rtmTaskLists, series In list.TaskCollection _
                                             Where Not IsNothing(list.TaskCollection) _
                                             Select series).ToList
            End If
            MyLog.Log("Get Outlook tasks")
            'get outlook tasks
            Dim ns As Outlook.NameSpace

            ns = MyApp.Application.GetNamespace("MAPI")

            outlookTasksAll = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderTasks).Items

            outlookTasks = (From outlookTask As Outlook.TaskItem In outlookTasksAll _
                                Where outlookTask.Status <> Outlook.OlTaskStatus.olTaskComplete _
                                ).ToList

            TotalTasksCounts = rtmTaskSeries.Count + outlookTasks.Count

            CreateInMemoryTaskLists()

            MyLog.Log("Finished loading data")
            RaiseEvent AfterDataLoad(TotalTasksCounts)
        Catch ex As Exception
            MyLog.Log("Error loading data", ex)
            Throw ex
        End Try
    End Sub

    ''' <summary>
    ''' Load data changed since the modifiedon date
    ''' </summary>
    ''' <param name="ModifiedOn"></param>
    ''' <remarks></remarks>
    '    Private Sub LoadData(ByVal ModifiedOn As Date)
    '        Try
    '            MyLog.Log("Begin Load Data since " + ModifiedOn.ToString)
    '            RaiseEvent BeforeDataLoad()
    '            'load modified rtm tasks 
    '            MyLog.Log("Get modified RTM Tasks")
    '            Dim rtmTasks As RTMnet.Tasks = myrtm.TasksGetList(, , ) 'get all tasks
    '            rtmTaskLists = (From list In rtmTasks.TaskListCollection _
    '                            Where Not IsNothing(list.TaskCollection) _
    '                            ).ToList

    '            'get items that have been modified or 
    '            'that are not in the synceditems list and incomplete
    '            'if it is completed and not in synced items then there is no point syncing it
    '            rtmTaskSeries = (From list In rtmTaskLists, series In list.TaskCollection _
    '                                         Where Date.Parse(series.Modified_raw) > ModifiedOn _
    '                                         OrElse (Not SyncedItems.Contains(series.TaskSeriesID, series.TaskInfo.First.ID) _
    '                                                 AndAlso series.TaskInfo.First.Completed = "") _
    '                                         Select series).ToList

    '            'get modified outlook Tasks
    '            Dim ns As Outlook.NameSpace
    '            ns = MyApp.Application.GetNamespace("MAPI")
    '            outlookTasksAll = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderTasks).Items

    '            outlookTasks = (From outlookTask As Outlook.TaskItem In outlookTasksAll _
    '                                Where outlookTask.LastModificationTime > ModifiedOn _
    '                                OrElse outlookTask.CreationTime > ModifiedOn _
    '                                 OrElse (Not SyncedItems.Contains(outlookTask) _
    '                                                 AndAlso outlookTask.Complete = False) _
    '                                ).ToList

    '            TotalTasksCounts = rtmTaskSeries.Count + outlookTasks.Count

    '#If DEBUG Then
    '            Try
    '                Dim tempTasks = (From outlooktask As Outlook.TaskItem In outlookTasksAll _
    '                             Where outlooktask.Subject = "o task 5").ToList
    '                Debug.Write(tempTasks.Count)
    '            Catch ex As Exception

    '            End Try
    '#End If

    '            CreateInMemoryTaskLists()

    '            MyLog.Log("Finished loading data")
    '            RaiseEvent AfterDataLoad(TotalTasksCounts)
    '        Catch ex As Exception
    '            MyLog.Log("Error loading data", ex)
    '            Throw ex
    '        End Try

    '    End Sub

    Private Sub CreateInMemoryTaskLists()
        'create the in-memory lists
        'Dim TasksMatched As List(Of CompoundTask) = (From list In rtmTaskLists, series In list.TaskCollection _
        '                    Where Not IsNothing(list.TaskCollection) _
        '                    Select MyTask = New Task(series, list.ListID) _
        '                    Join outlookTask As Outlook.TaskItem In outlookTasks _
        '                    On outlookTask.Subject.Trim Equals MyTask.Name.Trim _
        '                    Select New CompoundTask(MyTask, outlookTask)).ToList



        'outlookTaskNames = (From outlookTask As Outlook.TaskItem In outlookTasks Select outlookTask.Subject.Trim).ToList
        'TasksInRTM = (From series In rtmTaskSeries _
        '                    Where Not (outlookTaskNames).Contains(series.Name.Trim)).ToList
    End Sub
#Region "Sync Tasks"

    Private Sub SyncCategories()
        'clbProgress.SetItemChecked(0, True)
    End Sub

    Private Sub SyncMatchedItem(ByVal MatchedItem As RTMOSync.CompoundTask, Optional ByVal syncItem As SyncItem = Nothing)
        Dim outlookTaskName As String = MatchedItem.OutlookTask.Subject
        Dim rtmTaskName As String = MatchedItem.MyTask.Name
        Dim ShouldSync As Boolean = True
        Dim outlookId As String = MatchedItem.OutlookTask.EntryID
        Dim taskSeriesId As String = MatchedItem.MyTask.Series.TaskSeriesID
        Dim taskid As String = MatchedItem.MyTask.Series.TaskInfo.First.ID '(0).ID
        Dim listid = MatchedItem.MyTask.ListId
        Dim rtmTaskId = MatchedItem.MyTask.Series.TaskInfo.First.ID
        Dim UseOutlookVersion As Boolean = False
        Dim UseRTMVersion As Boolean = False
        If IsFullSync Then
            'initial sync - use sync option
            If My.Settings.DuplicatesUseOutlook Then
                UseOutlookVersion = True
            End If
            If My.Settings.DuplicatesUseRTM Then
                UseRTMVersion = True
            End If
            If My.Settings.DuplicatesUseLatest Then
                If MatchedItem.OutlookTask.LastModificationTime < DateTime.Parse(MatchedItem.MyTask.Series.Modified_raw) Then
                    'use rtm's version
                    UseRTMVersion = True
                Else
                    'use outlook's version
                    UseOutlookVersion = True
                End If
            End If
        Else
            'incremental sync
            'determine which task to use
            If MatchedItem.OutlookTask.LastModificationTime < DateTime.Parse(MatchedItem.MyTask.Series.Modified_raw) Then
                'use rtm's version
                UseRTMVersion = True
            Else
                'use outlook's version
                UseOutlookVersion = True
            End If
            'todo should test to see if both tasks were changed           
        End If

        If UseOutlookVersion Then
            SetRTMInfo(MatchedItem.MyTask.Series, MatchedItem.OutlookTask, listid)
        ElseIf UseRTMVersion Then
            SetOutlookInfo(MatchedItem.OutlookTask, MatchedItem.MyTask.Series)
            MatchedItem.OutlookTask.Save()
        End If
        Dim newSyncItem As New SyncItem(rtmTaskName, outlookId, listid, taskSeriesId, taskid)
        SyncedItems.Add(newSyncItem)
    End Sub

    Private Sub SyncMatchedItems()
        Dim OutlookTasksToDelete As New List(Of Integer)
        Dim outlookTasksToSave As New List(Of Outlook.TaskItem)
        Try
            MyLog.Log("Begin syncing duplicate items")
            RaiseEvent BeforeSyncDuplicateTasks()

            'sync matched items
            'loop only through tasks that have been modified

            Dim TasksMatched As List(Of CompoundTask)
            TasksMatched = (From list In rtmTaskLists, series In list.TaskCollection _
                                      Where Not IsNothing(list.TaskCollection) _
                                      Select MyTask = New Task(series, list.ListID) _
                                Join outlookTask As Outlook.TaskItem In outlookTasks _
                                    On outlookTask.Subject.Trim Equals MyTask.Name.Trim _
                                Select New CompoundTask(MyTask, outlookTask)).ToList


            For i As Integer = TasksMatched.Count - 1 To 0 Step -1
                Dim MatchedItem As RTMOSync.CompoundTask = TasksMatched(i)
                If Not IsNothing(MatchedItem) AndAlso _
                        Not IsNothing(MatchedItem.OutlookTask) AndAlso _
                        Not IsNothing(MatchedItem.MyTask) Then
                    SyncMatchedItem(MatchedItem)
                End If
                count += 1
                RaiseEvent ReportProgress(count)
            Next
            RaiseEvent AfterSyncDuplicateTasks()
            MyLog.Log("Finished syncing duplicate items")
        Catch ex As Exception
            MyLog.Log("Error syncing duplicate items", ex)
            Throw ex
        Finally
            ''lets delete the outlook tasks
            ''make sure we delete any tasks that we added
            'For Each i In OutlookTasksToDelete
            '    Try
            '        TasksMatched(i).OutlookTask.Delete()
            '    Catch ex As Exception
            '    End Try
            'Next
        End Try
    End Sub

    Private Sub CreateAndSyncRTM(ByVal outlookTask As Microsoft.Office.Interop.Outlook.TaskItem)
        'create sync item
        'add task to RTM
        Dim rtmTask = CreateRTM(outlookTask)
        Dim taskseriesid = rtmTask.TaskCollection(0).TaskSeriesID
        Dim listid = rtmTask.ListID
        Dim taskid = rtmTask.TaskCollection(0).TaskInfo.First.ID
        Dim newSyncItem As New SyncItem(outlookTask.Subject, outlookTask.EntryID, listid, taskseriesid, taskid)
        SyncedItems.Add(newSyncItem)
    End Sub
    Private Sub SyncRTMOnlyTasks()
        Try
            MyLog.Log("Begin syncing RTM only tasks")
            RaiseEvent BeforeSyncRTMOnlyTasks()

            Dim outlookTaskNames As List(Of String)
            Dim TasksInRTM As List(Of RTMnet.TaskSeries)
            'If IsInitialSync Then
            outlookTaskNames = (From outlookTask As Outlook.TaskItem In outlookTasks Select outlookTask.Subject.Trim).ToList
            TasksInRTM = (From series In rtmTaskSeries _
                           Where Not outlookTaskNames.Contains(series.Name.Trim)).ToList
            'Else
            '    'get the names of all the synced outlook tasks
            '    outlookTaskNames = (From outlooktask As Outlook.TaskItem In outlookTasksAll _
            '                        Where SyncedItems.Contains(outlooktask) _
            '                        Select outlooktask.Subject.Trim).ToList
            '    TasksInRTM = (From series In rtmTaskSeries _
            '                 Where Not outlookTaskNames.Contains(series.Name.Trim)).ToList
            'End If
            For Each item In TasksInRTM
                CreateAndSyncOutlook(item)
                count += 1
                RaiseEvent ReportProgress(count)
            Next
            'clbProgress.SetItemChecked(2, True)
            RaiseEvent AfterSyncRTMOnlyTasks()
            MyLog.Log("Finished syncing RTM only tasks")
        Catch ex As Exception
            MyLog.Log("Error syncing RTM only tasks", ex)
            Throw ex
        End Try
    End Sub
    Private Function CreateRTM(ByVal outlookTask As Outlook.TaskItem) As RTMnet.TaskList
        Try
            MyLog.Log("Adding task to RTM: " + outlookTask.Subject)
            Dim rtmTask = myrtm.TasksAdd(outlookTask.Subject)
            SetRTMInfo(rtmTask.TaskCollection(0), outlookTask, rtmTask.ListID)
            Return rtmTask
        Catch ex As Exception
            MyLog.Log("Failed to add task to RTM", ex)
            Throw ex
        End Try
    End Function
    Private Sub SetRTMInfo(ByRef series As RTMnet.TaskSeries, ByVal outlookTask As Outlook.TaskItem, ByVal ListId As String)
        Dim taskSeriesId = series.TaskSeriesID
        Dim taskId = series.TaskInfo.First.ID

        'set recurrence
        If outlookTask.IsRepeating Then
            Dim repeat = GetRepeatString(outlookTask)
            Dim results = myrtm.TasksSetRecurrence(ListId, taskSeriesId, taskId, repeat)
        Else
            myrtm.TasksRemoveRecurrence(ListId, taskSeriesId, taskId)
        End If

        'set tags
        Dim tags As String
        If Not IsNothing(outlookTask.Categories) AndAlso outlookTask.Categories.Length > 0 Then
            tags = outlookTask.Categories.Replace(" ", "")
            myrtm.TasksSetTags(ListId, taskSeriesId, taskId, tags)
        Else
            myrtm.TasksSetTags(ListId, taskSeriesId, taskId, "")
        End If

        'set due date only if set in outlook
        If outlookTask.DueDate = NoneDate Then
            myrtm.TasksRemoveDueDate(ListId, taskSeriesId, taskId)
        Else
            myrtm.TasksSetDueDate(ListId, taskSeriesId, taskId, RTMnet.Utils.DateToString(outlookTask.DueDate), "0", "1")
        End If

        'set priority
        Select Case outlookTask.Importance
            Case Outlook.OlImportance.olImportanceHigh
                myrtm.TasksSetPriority(ListId, taskSeriesId, taskId, "1")
            Case Outlook.OlImportance.olImportanceNormal
                myrtm.TasksSetPriority(ListId, taskSeriesId, taskId, "2")
            Case Outlook.OlImportance.olImportanceLow
                myrtm.TasksSetPriority(ListId, taskSeriesId, taskId, "3")
        End Select

        

        'set completed flag
        If outlookTask.Complete Then
            'task was completed
            If String.IsNullOrEmpty(series.TaskInfo.First.Completed) Then
                myrtm.TasksComplete(ListId, taskSeriesId, taskId)
            End If
        Else
            'task was uncompleted
            If Not String.IsNullOrEmpty(series.TaskInfo.First.Completed) Then
                myrtm.TasksSetUncomplete(ListId, taskSeriesId, taskId)
            End If
        End If
    End Sub



    Private Function CreateOutlook(ByVal rtmTask As RTMnet.TaskSeries) As Outlook.TaskItem
        Try
            MyLog.Log("Adding task to Outlook: " + rtmTask.Name)
            Dim outlookTask As Outlook.TaskItem = MyApp.Application.CreateItem(Outlook.OlItemType.olTaskItem)
            SetOutlookInfo(outlookTask, rtmTask)
            Return outlookTask
        Catch ex As Exception
            MyLog.Log("Failed to add outlook task", ex)
            Throw ex
        End Try
    End Function
    Private Sub CreateAndSyncOutlook(ByVal series As RTMnet.TaskSeries)
        Dim rtmTaskName As String = series.Name
        Dim taskseriesid As String = series.TaskSeriesID
        Dim listid As String = GetListId(series)
        Dim taskid = series.TaskInfo.First.ID

        'add task to outlook
        Dim outlookTask As Outlook.TaskItem = CreateOutlook(series)
        outlookTask.Save()
        Dim outlookid = outlookTask.EntryID

        'create sync item
        Dim newSyncItem As New SyncItem(rtmTaskName, outlookid, listid, taskseriesid, taskid)
        SyncedItems.Add(newSyncItem)
    End Sub
    Private Sub SyncOutlookOnlyTasks()
        Try
            MyLog.Log("Begin syncing outlook only tasks")
            RaiseEvent BeforeSyncOutlookOnlyTasks()

            Dim rtmTasksNames As List(Of String)
            rtmTasksNames = (From list In rtmTaskLists, series In list.TaskCollection _
                                               Where Not IsNothing(list.TaskCollection) _
                                               Select series.Name.Trim).ToList
            Dim TasksInOutlook As List(Of Outlook.TaskItem)
            TasksInOutlook = (From outlookTask As Outlook.TaskItem In outlookTasks _
                                                        Where Not (rtmTasksNames).Contains(outlookTask.Subject.Trim)).ToList

            For Each item In TasksInOutlook
                CreateAndSyncRTM(item)
                count += 1
                RaiseEvent ReportProgress(count)
            Next
            'Me.clbProgress.SetItemChecked(3, True)
            RaiseEvent AfterSyncOutlookOnlyTasks()
            MyLog.Log("Finished syncing outlook only tasks")
        Catch ex As Exception
            MyLog.Log("Error syncing outlook only tasks", ex)
            Throw ex
        End Try
    End Sub
    Private Shared Sub SetOutlookTags(ByRef outlookTask As Outlook.TaskItem, ByVal rtmTask As RTMnet.TaskSeries)
        If Not IsNothing(rtmTask.Tags.TagCollection) AndAlso rtmTask.Tags.TagCollection.Count > 0 Then
            Dim cats As String = ""
            For Each rtmTag In rtmTask.Tags.TagCollection
                If cats.Length = 0 Then
                    cats = rtmTag.Text
                Else
                    cats += "," + rtmTag.Text
                End If
            Next
            outlookTask.Categories = cats
        Else
            outlookTask.Categories = ""
        End If
    End Sub
    Private Sub SetOutlookDueDate(ByRef outlookTask As Outlook.TaskItem, ByVal rtmTask As RTMnet.TaskSeries)
        'set due date
        If Not IsNothing(rtmTask.TaskInfo) AndAlso (rtmTask.TaskInfo.Count > 0) Then
            If rtmTask.TaskInfo.First.Due_raw.Length > 0 Then
                outlookTask.DueDate = rtmTask.TaskInfo.First.Due
            Else
                outlookTask.DueDate = NoneDate
            End If
        End If
    End Sub
    Private Shared Sub SetOutlookPriority(ByRef outlookTask As Outlook.TaskItem, ByVal rtmTask As RTMnet.TaskSeries)
        'set priority
        If Not IsNothing(rtmTask.TaskInfo) AndAlso (rtmTask.TaskInfo.Count > 0) Then
            Select Case rtmTask.TaskInfo.First.Priority
                Case "1"
                    outlookTask.Importance = Outlook.OlImportance.olImportanceHigh
                Case "2"
                    outlookTask.Importance = Outlook.OlImportance.olImportanceNormal
                Case "3"
                    outlookTask.Importance = Outlook.OlImportance.olImportanceLow
                Case Else
                    outlookTask.Importance = Outlook.OlImportance.olImportanceNormal
            End Select
        End If
    End Sub
    Private Sub SetOutlookRecurrencePattern(ByRef outlookTask As Outlook.TaskItem, ByVal rtmTask As RTMnet.TaskSeries)
        If Not IsNothing(rtmTask.Recurrence) Then
            Debug.WriteLine(rtmTask.Recurrence.Repeat)
            outlookTask.ClearRecurrencePattern()
            Dim recurrence As Outlook.RecurrencePattern = outlookTask.GetRecurrencePattern()
            Select Case rtmTask.Recurrence.Freq
                Case RTMnet.Recurrence.Frequencies.DAILY
                    recurrence.RecurrenceType = Outlook.OlRecurrenceType.olRecursDaily
                Case RTMnet.Recurrence.Frequencies.WEEKLY
                    recurrence.RecurrenceType = Outlook.OlRecurrenceType.olRecursWeekly
                Case RTMnet.Recurrence.Frequencies.MONTHLY
                    recurrence.RecurrenceType = Outlook.OlRecurrenceType.olRecursMonthly
                Case Else
                    Debug.WriteLine(rtmTask.Recurrence.Repeat & rtmTask.Recurrence.Freq)
            End Select

            'get day of week mask
            If rtmTask.Recurrence.DayOfWeekMask > 0 Then
                recurrence.DayOfWeekMask = rtmTask.Recurrence.DayOfWeekMask
            End If

            'get end date
            If rtmTask.Recurrence.Until <> DateTime.MinValue Then
                recurrence.PatternEndDate = rtmTask.Recurrence.Until
            End If

            'set interval
            If rtmTask.Recurrence.Interval > 0 Then
                'todo shoudl we do this check???
                recurrence.Interval = rtmTask.Recurrence.Interval
            End If
            'set count
            If rtmTask.Recurrence.Count > 0 Then
                recurrence.Occurrences = rtmTask.Recurrence.Count
            End If
            If rtmTask.Recurrence.Until = Date.MinValue AndAlso recurrence.NoEndDate = False Then
                'change rtm recurrent string to use until instead of count
                Dim listid As String = GetListId(rtmTask)
                outlookTask.Save()
                Dim repeat = GetRepeatString(outlookTask)
                myrtm.TasksSetRecurrence(listid, rtmTask.TaskSeriesID, rtmTask.TaskInfo.First.ID, repeat)
            End If
            If rtmTask.TaskInfo.First.Due_raw.Length = 0 Then
                'set a due date in recurring items in rtm since outlook recurring tasks always have a due date
                outlookTask.Save()
                Dim listid As String = GetListId(rtmTask)
                myrtm.TasksSetDueDate(listid, rtmTask.TaskSeriesID, rtmTask.TaskInfo.First.ID, RTMnet.Utils.DateToString(outlookTask.DueDate), "0", "1")
            End If
        Else
            'remove recurrence
            outlookTask.ClearRecurrencePattern()
        End If
    End Sub
    Private Shared Sub SetOutlookComplete(ByRef outlookTask As Outlook.TaskItem, ByVal rtmTask As RTMnet.TaskSeries)
        'set outlook complete
        If String.IsNullOrEmpty(rtmTask.TaskInfo.First.Completed) Then
            'task is uncompleted
            If outlookTask.Complete Then
                outlookTask.Complete = False
            End If
        Else
            'task is completed
            If outlookTask.Complete = False Then
                outlookTask.MarkComplete() 'Complete = True
            End If
        End If
    End Sub
    Private Sub SetOutlookInfo(ByRef outlookTask As Outlook.TaskItem, ByVal rtmTask As RTMnet.TaskSeries)
        outlookTask.Subject = rtmTask.Name
        SetOutlookTags(outlookTask, rtmTask)
        SetOutlookDueDate(outlookTask, rtmTask)
        SetOutlookPriority(outlookTask, rtmTask)
        SetOutlookComplete(outlookTask, rtmTask)
        SetOutlookRecurrencePattern(outlookTask, rtmTask)
    End Sub

#End Region

#Region "Helper methods"
    Private Function GetRepeatString(ByVal outlookTask As Outlook.TaskItem) As String
        Try
            If outlookTask.IsRepeating = False Then
                Return String.Empty
            Else

                Dim recurrence = outlookTask.GetRecurrencePattern
                Dim repeat As String = ""
                'set interval
                Dim interval = recurrence.Interval
                'set type
                Select Case recurrence.RecurrenceType
                    Case Outlook.OlRecurrenceType.olRecursDaily
                        repeat = String.Format("Every {0} Day", interval)
                    Case Outlook.OlRecurrenceType.olRecursWeekly
                        repeat = String.Format("Every {0} week", interval)
                    Case Outlook.OlRecurrenceType.olRecursMonthNth
                        repeat = String.Format("Every {0} month", interval)
                    Case Outlook.OlRecurrenceType.olRecursMonthly
                        repeat = String.Format("Every {0} month", interval)
                    Case Outlook.OlRecurrenceType.olRecursYearly
                        repeat = String.Format("Every {0} year", interval)
                    Case Outlook.OlRecurrenceType.olRecursYearNth
                        repeat = String.Format("Every {0} year", interval)
                End Select

                'set day masks
                Dim daymask As String = GetDayMaskString(recurrence)
                If Not String.IsNullOrEmpty(daymask) Then
                    repeat = String.Format("Every {0}", daymask)
                End If

                'set end date
                If Not recurrence.NoEndDate Then
                    repeat += String.Format(" until {0}", RTMnet.Utils.DateToString(recurrence.PatternEndDate))
                End If

                ''set count
                'If (Not recurrence.NoEndDate) AndAlso (recurrence.Occurrences > 0) Then
                '    repeat += String.Format(" for {0} times", recurrence.Occurrences)
                'End If

                Return repeat
            End If
        Catch ex As Exception
            MyLog.Log("Error getting repeat string", ex)
            Throw ex
        End Try
    End Function

    Private Shared Function GetDayMaskString(ByVal recurrence As Outlook.RecurrencePattern) As String
        Try
            Dim daymask As String = ""
            If recurrence.DayOfWeekMask > 0 Then
                If (RTMnet.Recurrence.DayOfWeeks.SU And recurrence.DayOfWeekMask) = RTMnet.Recurrence.DayOfWeeks.SU Then
                    If String.IsNullOrEmpty(daymask) Then
                        daymask = "sunday"
                    Else
                        daymask += " and sunday"
                    End If
                End If
                If (RTMnet.Recurrence.DayOfWeeks.MO And recurrence.DayOfWeekMask) = RTMnet.Recurrence.DayOfWeeks.MO Then
                    If String.IsNullOrEmpty(daymask) Then
                        daymask = "monday"
                    Else
                        daymask += " and monday"
                    End If
                End If
                If (RTMnet.Recurrence.DayOfWeeks.TU And recurrence.DayOfWeekMask) = RTMnet.Recurrence.DayOfWeeks.TU Then
                    If String.IsNullOrEmpty(daymask) Then
                        daymask = "tuesday"
                    Else
                        daymask += " and tuesday"
                    End If
                End If
                If (RTMnet.Recurrence.DayOfWeeks.WE And recurrence.DayOfWeekMask) = RTMnet.Recurrence.DayOfWeeks.WE Then
                    If String.IsNullOrEmpty(daymask) Then
                        daymask = "wednesday"
                    Else
                        daymask += " and wednesday"
                    End If
                End If
                If (RTMnet.Recurrence.DayOfWeeks.TH And recurrence.DayOfWeekMask) = RTMnet.Recurrence.DayOfWeeks.TH Then
                    If String.IsNullOrEmpty(daymask) Then
                        daymask = "thursday"
                    Else
                        daymask += " and thursday"
                    End If
                End If
                If (RTMnet.Recurrence.DayOfWeeks.FR And recurrence.DayOfWeekMask) = RTMnet.Recurrence.DayOfWeeks.FR Then
                    If String.IsNullOrEmpty(daymask) Then
                        daymask = "friday"
                    Else
                        daymask += " and friday"
                    End If
                End If
                If (RTMnet.Recurrence.DayOfWeeks.SA And recurrence.DayOfWeekMask) = RTMnet.Recurrence.DayOfWeeks.SA Then
                    If String.IsNullOrEmpty(daymask) Then
                        daymask = "saturday"
                    Else
                        daymask += " and saturday"
                    End If
                End If
            End If
            Return daymask
        Catch ex As Exception
            MyLog.Log("Error getting day mask", ex)
            Throw ex
        End Try
    End Function

    Private Function GetListId(ByVal series As RTMnet.TaskSeries) As String
        Dim results = From list In rtmTaskLists Where list.TaskCollection.Contains(series)
        If results.Count > 0 Then
            Return results(0).ListID
        Else
            Return 0
        End If
    End Function
#End Region

End Class